﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;

using alib;
using alib.Debugging;
using alib.Enumerable;

namespace agree
{
	public partial class ParseControl : ChartControl<ParseControl, ChartSpan>
	{
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public sealed class ChartCell : _chart_cell
		{
			public struct _multi_cell_enum : IEnumerator<IChartObj>
			{
				int i, i_incr, j, j_incr;
				ChartCell cc;
				IChartObj cur;
				ChartCell[][] rgcc;

				public _multi_cell_enum(ChartCell[][] rgcc, int i_min, int i_incr, int j_min, int j_incr)
				{
					this.i = i_min;
					this.i_incr = i_incr;
					this.j = j_min;
					this.j_incr = j_incr;
					this.rgcc = rgcc;
					this.cc = rgcc[i][j];
					this.cur = null;
				}

				public bool MoveNext()
				{
					while ((cur = cur == null ? cc.m_first : cur.Next) == null)
					{
						if ((j += j_incr) < 0 || (i += i_incr) < 0 || i >= rgcc.Length)
						{
							this.rgcc = null;
							return false;
						}
						ChartCell[] arr = rgcc[i];
						if (j >= arr.Length)
						{
							this.rgcc = null;
							return false;
						}
						cc = arr[j];
						cur = null;
					}
					return true;
				}

				public IChartObj Current { get { return cur; } }
				object IEnumerator.Current { get { return cur; } }
				public void Reset() { throw not.impl; }
				public void Dispose() { cur = null; cc = null; rgcc = null; }
			};

			public struct _enum_all_edges : IEnumerator<IChartObj>
			{
				ChartCell[][] rgcc;
				IChartObj cur;
				int i_col, i_span;

				public _enum_all_edges(ChartCell[][] ch)
				{
					this.rgcc = ch;
					this.cur = null;
					this.i_col = this.i_span = 0;
				}

				public void Reset()
				{
					i_col = 0;
					cur = null;
				}

				public bool MoveNext()
				{
					do
					{
						ChartCell[] cc = rgcc[i_col];
						do
							while ((cur = cur == null ? cc[i_span].m_first : cur.Next) != null)
								if (cur.WaitActive())
									return true;
						while (++i_span < cc.Length);
						i_span = 0;
					}
					while (++i_col < rgcc.Length);
					return false;
				}

				public IChartObj Current { get { return cur; } }
				object IEnumerator.Current { get { return cur; } }
				public void Dispose() { cur = null; rgcc = null; }
			};
		};

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		protected override bool CheckRuleGeometry(IActiveRule ir, ref ChartSpan new_coverage)
		{
			Rule base_rule = ir.BaseRule;
			int rule_arity = base_rule.DaughterArity;
			if (rule_arity == 1)
				return new_coverage.CoverageCount == spanning_count || !base_rule.IsSpanOnly;

			int i_start = new_coverage.StartIndex;
			int i_end = new_coverage.EndIndex;
			int i_key = base_rule.KeyDaughterIndex;

			// if the edge will not fit, don't bother creating the active edge.
			if (i_start < i_key || i_end > spanning_count - (rule_arity - i_key))
				return false;

			/// Since active edges only grow in one direction, there's no point in creating a span-only 
			/// active edge which does not abut the beginning (or end) of the chart.
			return !base_rule.IsSpanOnly || (i_key == 0 ? i_start == 0 : i_end == spanning_count - 1);		// ???
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		protected override void AddActiveEdge(ActiveEdge ae)
		{
			ChartSpan csp = ae.ctx;
			bool f_right = ae.f_rightwards;
			ae.ArityLimit = (f_right ? spanning_count - csp.EndIndex : csp.StartIndex + 1)
						- ae.rule.DaughterArity + ae.c_partial;

			Debug.Assert(ae.ArityLimit > 0);

			rgns[csp.NotificationIndex(f_right)].Subscribe(ae);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		protected override IEnumerator<IChartObj> GetPassiveForActive(ActiveEdge ae)
		{
			int i = ae.ctx.NotificationIndex(ae.f_rightwards);
			return (i & 1) == 0 ?
				new ChartCell._multi_cell_enum(chart, 0, 1, i >> 1, -1) :
				new ChartCell._multi_cell_enum(chart, (i >> 1) + 1, 0, 0, 1);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		protected override void SendToPassiveEdges(IChartObj<ChartSpan> po)
		{
			ChartSpan csp = po.ProximalContext;

			/// send the edge to interested parties:
			/// 1. parties for which the chart object is to the right
			if (csp.StartIndex > 0)
				rgns[((csp.StartIndex - 1) << 1) + 1].SendToAllSubscribers(po);

			/// 2. parties for which the chart object is to the left
			if (csp.EndIndex < spanning_count - 1)
				rgns[csp.EndIndex << 1].SendToAllSubscribers(po);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		protected override bool CheckPassiveAccessibility(ArrayTfs new_mother, ref ChartSpan ctx_new)
		{
			/// not relevant in parsing mode
			return true;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		protected override IChartObj CreateChartObj(
			Rule rule,
			ArrayTfs tfs,
			IReadOnlyList<IChartObj> rgo,
			ChartSpan csp,
			StartSymbol[] ss_matches)
		{
			return tfs.SemanticsRestricted ? (IChartObj)
				new pxo_nary_pack(this, rule, tfs, rgo, csp, ss_matches) :
				new pxo_nary(this, rule, tfs, rgo, csp, ss_matches);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		protected override void AddChartObj(IChartObj<ChartSpan> po)
		{
			ChartSpan ctx = po.ProximalContext;
			chart[ctx.StartIndex][ctx.CoverageCount - 1].Add(po, ref this.next_obj_id);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		protected override IEnumerator<IChartObj> GetPackingCandidates(IChartObj _po)
		{
			var po = (IChartObj<ChartSpan>)_po;
			ChartSpan ctx = po.ProximalContext;
			return new ChartCell._enum_x(chart[ctx.StartIndex][ctx.CoverageCount - 1], po);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		protected override IEnumerator<IChartObj> ItemEdges(ChartSpan ctx)
		{
			return new ChartCell._enum(chart[ctx.StartIndex][ctx.CoverageCount - 1]);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public override IEnumerable<IChartObj> AllEdges()
		{
			Wait();

			var e = new ChartCell._enum_all_edges(chart);
			while (e.MoveNext())
				yield return e.Current;
		}

		public override String ToString()
		{
			return "Parse chart ";/* +ctrl.SysObjName + " "*/ //+ ctrl.ts_input.Text;
		}
	};
}
